package sso.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import sso.util.JwtUtils;
import sso.util.WebUtils;

import java.util.*;

/**
 * @description:
 * @author: LiuXingYu
 * @date: 2021/7/26 17:32
 */

/**构建配置安全对象
 * 1)认证规则(哪些资源必须认证才可访问)
 * 2)加密规则(添加用户时密码写到了数据库,登录时要将输入的密码与数据查询出的密码进行比对)
 * 3)认证成功怎么处理?(跳转页面,返回json)
 * 4)认证失败怎么处理?(跳转页面,返回json)
 * 5)没有登录就去访问资源系统怎么处理?(返回登录页面,返回json)
 * */
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    /**密码加密*/
    @Bean
    public BCryptPasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
    }

    /**
     * 定义认证规则
     * @param http 安全对象
     * @throws Exception
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
//        super.configure(http);
        // 1.关闭跨域攻击
        http.csrf().disable();
        // 2.配置form认证
        http.formLogin()
//                .defaultSuccessUrl("/login")
                .successHandler(authenticationSuccessHandler())
                .failureHandler(authenticationFailureHandler());
        //假如某个资源必须认证才可访问,那没有认证怎么办?(返回json)
        http.exceptionHandling()
                .authenticationEntryPoint(authenticationEntryPoint());

        http.authorizeRequests()
                .anyRequest().authenticated();
    }

    //认证成功处理器
    private AuthenticationSuccessHandler authenticationSuccessHandler() {
        System.out.println("认证成功");
        // 当接口中只有一个方法时，改为lambda表达式
        return (request, response, authentication)  -> {
            Map<String, Object> map = new HashMap<>();
            map.put("state", 200);
            map.put("message", "Login ok");
            //思考除了返回这些信息，还要返回什么? (JWT令牌)
            Map<String, Object> userInfo = new HashMap<>();
            //获取用户对象，此对象为登录成功以后封装了登录信息的对象
            User user = (User) authentication.getPrincipal();
            String username = user.getUsername();
            userInfo.put("username", username);
            //获取用户权限封装到userInfo中
            List<String> authorities = new ArrayList<>();
            user.getAuthorities().forEach((authority) -> {
                authorities.add(authority.getAuthority());
            });
            userInfo.put("authorities", authorities);
            String token = JwtUtils.generatorToken(userInfo);
            map.put("token", token);
            WebUtils.WriteJsonToClient(response,map);
        };
    }

    //认证失败处理器
    private AuthenticationFailureHandler authenticationFailureHandler(){
        System.out.println("认证失败");
        return (request, response, exception) -> {
                Map<String, Object> map = new HashMap<>();
                map.put("state", "500");
                map.put("message", "username or password error");
                WebUtils.WriteJsonToClient(response, map);
        };
    }

    // 假如没有登录访问资源时给出提示
    private AuthenticationEntryPoint authenticationEntryPoint(){
        return (request, response, exception) -> {
            Map<String, Object> map = new HashMap<>();
            map.put("state", 401);
            map.put("massage","请先登录再访问");
            WebUtils.WriteJsonToClient(response, map);
        };
    }
}
